package wiki.kaizen.cloud.security.session.config;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.iherus.shiro.cache.redis.RedisCache;
import org.iherus.shiro.cache.redis.RedisCacheConfigFactory;
import org.iherus.shiro.cache.redis.RedisCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import wiki.kaizen.cloud.security.session.SecurityGlobalConst;
import wiki.kaizen.cloud.security.session.properties.SecurityProperties;
import wiki.kaizen.cloud.security.session.support.KaizenSecurityManager;
import wiki.kaizen.cloud.security.session.support.KaizenSessionManager;
import wiki.kaizen.cloud.security.session.support.listener.LogoutListener;
import wiki.kaizen.cloud.security.session.support.listener.SessionValidateListener;
import wiki.kaizen.cloud.security.session.support.realm.IAuthorizingRealm;
import wiki.kaizen.cloud.security.session.support.realm.PasswordCredentialsMatcher;
import wiki.kaizen.cloud.security.session.support.service.LockedUserService;
import wiki.kaizen.cloud.security.session.support.service.RealmService;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 管理器配置类
 * @author xeepoo
 * */
@Configuration
public class ManagerConfig {

    private final SecurityProperties securityProperties;

    @Autowired
    public ManagerConfig(SecurityProperties securityProperties) {
        this.securityProperties = securityProperties;
    }


    /**
     * 缓存管理器
     * */
    @Bean
    public RedisCacheManager cacheManager(RedisCacheConfigFactory factory){

        RedisCacheManager cacheManager = new RedisCacheManager();

        cacheManager.setConfigFactory(factory);
        cacheManager.setShiroCacheKeyPrefix(SecurityGlobalConst.SHIRO_PREFIX);
        return cacheManager;

    }

    /*
    * session dao
    * */
    @Bean
    public EnterpriseCacheSessionDAO sessionDAO(RedisCacheManager cacheManager){
        EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
        sessionDAO.setCacheManager(cacheManager);
        return sessionDAO;
    }

    /**
     * session manager
     * */
    @Bean
    public KaizenSessionManager sessionManager(
        EnterpriseCacheSessionDAO sessionDAO,
        @Qualifier("sessionId") SimpleCookie sessionid,
        @Nullable List<SessionListener> listeners,
        @Nullable List<SessionValidateListener> validateListeners
    ){

        KaizenSessionManager sessionManager = new KaizenSessionManager();
        sessionManager.setSessionDAO(sessionDAO);
        //监听器配置
        sessionManager.setSessionListeners(listeners);
        sessionManager.setValidates(validateListeners);

        sessionManager.setGlobalSessionTimeout(securityProperties.getGlobalSessionTimeout());
        //删除无效的session
        sessionManager.setDeleteInvalidSessions(true);
        //检测过期session
        sessionManager.setSessionValidationSchedulerEnabled(true);
        //有效性验证时间间隔 半小时验证一次
        sessionManager.setSessionValidationInterval(securityProperties.getSessionValidationInterval());

        sessionManager.setSessionIdCookieEnabled(true);
        sessionManager.setSessionIdCookie(sessionid);
        sessionManager.setSessionIdUrlRewritingEnabled(false);

        return sessionManager;

    }


    /**
     * 安全 manager
     * */
    @Bean
    public KaizenSecurityManager securityManager(
        KaizenSessionManager sessionManager,
        RedisCacheManager redisCacheManager,
        CookieRememberMeManager rememberMeManager,
        List<Realm> realms,
        @Nullable  List<LogoutListener> logoutListeners
    ){
        KaizenSecurityManager securityManager = new KaizenSecurityManager();
        securityManager.setLogouts(logoutListeners);
        securityManager.setCacheManager(redisCacheManager);
        securityManager.setRememberMeManager(rememberMeManager);
        securityManager.setSessionManager(sessionManager);
        securityManager.setRealms(realms);
        return securityManager;
    }

    /**
     * sessionid cookie
     * */
    @Bean(name = "sessionId")
    public SimpleCookie sessionIdCookie(){
        SecurityProperties.Cookie prop = securityProperties.getSessionid();
        return setProperties(prop);
    }

    /**
     * rememberme cookie
     * */
    @Bean(name = "rememberme")
    public SimpleCookie simpleCookie(){
        SecurityProperties.Cookie prop = securityProperties.getRemember();
        return setProperties(prop);

    }

    /**
     *
     * rememberme manager
     * */
    @Bean
    public CookieRememberMeManager rememberMeManager(@Qualifier("rememberme") SimpleCookie rememberme){
        CookieRememberMeManager manager = new CookieRememberMeManager();
        manager.setCookie(rememberme);
        return manager;

    }
    /**
     * auth realm
     * */
    @SuppressWarnings("unchecked")
    @Bean
    public IAuthorizingRealm realm(
        PasswordCredentialsMatcher password ,
        RedisCacheManager cacheManager,
        RealmService realmService,
        RedisCache<Object, AuthenticationInfo> authenticCache,
        RedisCache<Object, AuthorizationInfo> authorizeCache
    ){
        IAuthorizingRealm realm = new IAuthorizingRealm();

        realm.setRealmService(realmService);
        if(securityProperties.getEnableCacheAuth()){
            //使用缓存
            realm.setCachingEnabled(true);
            //启用身份验证缓存 缓存AuthenticationInfo
            realm.setAuthenticationCachingEnabled(true);
            realm.setAuthenticationCache(authenticCache);
            //启用身份验证缓存 AuthorizationInfo
            realm.setAuthorizationCachingEnabled(true);
            realm.setAuthorizationCache(authorizeCache);

            realm.setCacheManager(cacheManager);
        }
        realm.setCredentialsMatcher(password);

        return realm;
    }

    /*
    * 密码 加密及 密码错误试错限制
    * */
    @SuppressWarnings("unchecked")
    @Bean
    public PasswordCredentialsMatcher password(
        LockedUserService lockedUserService,
        RedisCache<String, AtomicInteger> authfaileCache
    ){
        SecurityProperties.AccessAccount account = securityProperties.getAccount();
        PasswordCredentialsMatcher password = new PasswordCredentialsMatcher();
        password.setUserService(lockedUserService);
        password.setCache(authfaileCache);
        password.setMax(account.getTryMax());
        password.setHashAlgorithmName(account.getHashAlgorithmName());
        password.setHashIterations(account.getHashIterations());
        //password.setStoredCredentialsHexEncoded(true);
        return password;
    }

    /*
    * 处理cookie
    * */
    private SimpleCookie setProperties(SecurityProperties.Cookie prop){
        SimpleCookie cookie = new SimpleCookie();
        cookie.setName(prop.getName());
        cookie.setMaxAge(prop.getMaxAge());
        cookie.setHttpOnly(prop.getHttpOnly());
        cookie.setPath(prop.getPath());
        cookie.setDomain(prop.getDomain());
        return cookie;
    }


}
